Prevent some possible XSS attacks where user input makes its way into JSON and contains </script> tags.

Andrew Cantino 10 years ago
parent
commit
3eaa9272b1

+ 3 - 3
app/views/agents/agent_views/peak_detector_agent/_show.html.erb

@@ -17,9 +17,9 @@
17 17
       <script>
18 18
         $(function() {
19 19
           var $chart = $(".chart-container.group-<%= index.to_s %>").last();
20
-          var data = <%= data.map {|count, time| { :x => time.to_i, :y => count.to_i } }.to_json.html_safe %>;
21
-          var peaks = <%= ((@agent.memory[:peaks] && @agent.memory[:peaks][group_name]) || []).to_json.html_safe %>;
22
-          var name = <%= group_name.to_json.html_safe %>;
20
+          var data = <%= Utils.jsonify(data.map {|count, time| { :x => time.to_i, :y => count.to_i } }) %>;
21
+          var peaks = <%= Utils.jsonify((@agent.memory[:peaks] && @agent.memory[:peaks][group_name]) || []) %>;
22
+          var name = <%= Utils.jsonify(group_name) %>;
23 23
 
24 24
           renderGraph($chart, data, peaks, name);
25 25
         });

+ 2 - 2
app/views/agents/agent_views/twitter_stream_agent/_show.html.erb

@@ -35,8 +35,8 @@
35 35
         <script>
36 36
           $(function() {
37 37
             var $chart = $(".chart-container.group-<%= index.to_s %>").last();
38
-            var data = <%= group.select {|e| e.payload[:count].present? }.sort_by {|e| e.payload[:time] }.map {|e| { :x => e.payload[:time].to_i, :y => e.payload[:count].to_i } }.to_json.html_safe %>;
39
-            var name = <%= filter.to_json.html_safe %>;
38
+            var data = <%= Utils.jsonify(group.select {|e| e.payload[:count].present? }.sort_by {|e| e.payload[:time] }.map {|e| { :x => e.payload[:time].to_i, :y => e.payload[:count].to_i } }) %>;
39
+            var name = <%= Utils.jsonify(filter) %>;
40 40
 
41 41
             renderGraph($chart, data, [], name);
42 42
           });

+ 2 - 2
app/views/agents/show.html.erb

@@ -132,12 +132,12 @@
132 132
 
133 133
             <p>
134 134
               <b>Options:</b>
135
-              <pre><%= JSON.pretty_generate @agent.options || {} %></pre>
135
+              <pre><%= Utils.pretty_jsonify @agent.options || {} %></pre>
136 136
             </p>
137 137
 
138 138
             <p>
139 139
               <b>Memory:</b>
140
-              <pre><%= JSON.pretty_generate @agent.memory || {} %></pre>
140
+              <pre><%= Utils.pretty_jsonify @agent.memory || {} %></pre>
141 141
             </p>
142 142
           </div>
143 143
         </div>

+ 1 - 1
app/views/events/show.html.erb

@@ -7,7 +7,7 @@
7 7
 
8 8
       <p>
9 9
         <b>Payload:</b>
10
-        <pre><%= JSON.pretty_generate @event.payload || {} %></pre>
10
+        <pre><%= Utils.pretty_jsonify @event.payload || {} %></pre>
11 11
       </p>
12 12
 
13 13
       <% if @event.lat && @event.lng %>

+ 14 - 2
lib/utils.rb

@@ -52,7 +52,19 @@ module Utils
52 52
     end
53 53
   end
54 54
 
55
-  def self.jsonify(thing)
56
-    thing.to_json.gsub('</', '<\/').html_safe
55
+  # Output JSON that is ready for inclusion into HTML.  If you simply use to_json on an object, the
56
+  # presence of </script> in the valid JSON can break the page and allow XSS attacks.
57
+  # Optionally, pass `:skip_safe => true` to not call html_safe on the output.
58
+  def self.jsonify(thing, options = {})
59
+    json = thing.to_json.gsub('</', '<\/')
60
+    if !options[:skip_safe]
61
+      json.html_safe
62
+    else
63
+      json
64
+    end
65
+  end
66
+
67
+  def self.pretty_jsonify(thing)
68
+    JSON.pretty_generate(thing).gsub('</', '<\/')
57 69
   end
58 70
 end

+ 22 - 0
spec/lib/utils_spec.rb

@@ -55,4 +55,26 @@ describe Utils do
55 55
       Utils.values_at({ :foo => { :bar => "escape this!?" }}, "escape $.foo.bar").should == ["escape+this%21%3F"]
56 56
     end
57 57
   end
58
+
59
+  describe "#jsonify" do
60
+    it "escapes </script> tags in the output JSON" do
61
+      cleaned_json = Utils.jsonify(:foo => "bar", :xss => "</script><script>alert('oh no!')</script>")
62
+      cleaned_json.should_not include("</script>")
63
+      cleaned_json.should include("<\\/script>")
64
+    end
65
+
66
+    it "html_safes the output unless :skip_safe is passed in" do
67
+      Utils.jsonify({:foo => "bar"}).should be_html_safe
68
+      Utils.jsonify({:foo => "bar"}, :skip_safe => false).should be_html_safe
69
+      Utils.jsonify({:foo => "bar"}, :skip_safe => true).should_not be_html_safe
70
+    end
71
+  end
72
+
73
+  describe "#pretty_jsonify" do
74
+    it "escapes </script> tags in the output JSON" do
75
+      cleaned_json = Utils.pretty_jsonify(:foo => "bar", :xss => "</script><script>alert('oh no!')</script>")
76
+      cleaned_json.should_not include("</script>")
77
+      cleaned_json.should include("<\\/script>")
78
+    end
79
+  end
58 80
 end